package com.elephant.leil.generator.service;

import com.elephant.leil.generator.common.TemplateNameStorage;
import com.elephant.leil.generator.exception.TableModelNullException;
import com.elephant.leil.generator.mapper.TableStructureMapper;
import com.elephant.leil.generator.model.Column;
import com.elephant.leil.generator.model.TableModel;
import com.elephant.leil.generator.utils.PathUtils;
import com.elephant.leil.generator.utils.StringConvertUtils;
import com.elephant.leil.generator.utils.TypeUtils;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class CodeGenerateService {

    @Autowired
    TableStructureMapper tableStructureMapper;

    /**
     * 生成所有文件
     * @param tableName         表名称
     * @param prefix            表前缀
     * @param basePackage       基础包路径
     * @throws TableModelNullException      table model异常
     * @throws TemplateException
     * @throws IOException
     */
    public void generateAllFile(String tableName, String prefix, String basePackage) throws TableModelNullException, TemplateException, IOException {
        this.generateListPageFile(tableName, prefix);
        this.generateEntityFile(tableName, prefix, basePackage);
        this.generateServiceInterfaceFile(tableName, prefix, basePackage);
        this.generateServiceImplFile(tableName, prefix, basePackage);
        this.generateJavaMapperFile(tableName, prefix, basePackage);
        this.generateXmlMapperFile(tableName, prefix, basePackage);
        this.generateControllerFile(tableName, prefix, basePackage);
    }

    /**
     * 生成列表页
     * @throws IOException
     * @throws TemplateException
     */
    public void generateListPageFile(String tableName, String prefix) throws IOException, TemplateException, TableModelNullException {
        TableModel tableModel = this.initTableInfo(tableName, prefix);
        Map<String, Object> fillData = new HashMap<>();
        fillData.put("middleLineModelName", tableModel.getMiddleLineModelName());
        fillData.put("conditions", tableModel.getColumns());
        fillData.put("columns", tableModel.getColumns());
        String outPath = PathUtils.getProjectDir() + "generate/front/";
        String fileName = tableModel.getUpperCamelModelName() + ".vue";
        this.generateFile(TemplateNameStorage.LIST_PAGE_TPL, outPath, fileName, fillData);
    }


    /**
     * 生成实体类
     * @throws IOException
     * @throws TemplateException
     */
    public void generateEntityFile(String tableName, String prefix, String basePackage) throws IOException, TemplateException, TableModelNullException {
        TableModel tableModel = this.initTableInfo(tableName, prefix);
        Map<String, Object> fillData = this.getJavaFillData(tableModel, basePackage);
        String basePath = this.basePackageToPath(basePackage);
        String entityPath = basePath + "/entity";
        String outPath = PathUtils.getProjectDir() + "generate/" + entityPath + "/";
        String fileName = tableModel.getUpperCamelModelName() + ".java";
        this.generateFile(TemplateNameStorage.ENTITY_TPL, outPath, fileName, fillData);
    }


    /**
     * 生成service接口
     * @throws IOException
     * @throws TemplateException
     */
    public void generateServiceInterfaceFile(String tableName, String prefix, String basePackage) throws IOException, TemplateException, TableModelNullException {
        TableModel tableModel = this.initTableInfo(tableName, prefix);
        Map<String, Object> fillData = this.getJavaFillData(tableModel, basePackage);
        String basePath = this.basePackageToPath(basePackage);
        String servicePath = basePath + "/service";
        String outPath = PathUtils.getProjectDir() + "generate/" + servicePath + "/";
        String fileName = "I" + tableModel.getUpperCamelModelName() + "Service.java";
        this.generateFile(TemplateNameStorage.SERVICE_INTERFACE_TPL, outPath, fileName, fillData);
    }

    /**
     * 生成service接口实现类
     * @throws IOException
     * @throws TemplateException
     */
    public void generateServiceImplFile(String tableName, String prefix, String basePackage) throws IOException, TemplateException, TableModelNullException {
        TableModel tableModel = this.initTableInfo(tableName, prefix);
        Map<String, Object> fillData = this.getJavaFillData(tableModel, basePackage);
        String basePath = this.basePackageToPath(basePackage);
        String serviceImplPath = basePath + "/service/impl";
        String outPath = PathUtils.getProjectDir() + "generate/" + serviceImplPath + "/";
        String fileName = tableModel.getUpperCamelModelName() + "ServiceImpl.java";
        this.generateFile(TemplateNameStorage.SERVICE_IMPL_TPL, outPath, fileName, fillData);
    }

    /**
     * 生成JavaMapper文件
     * @throws IOException
     * @throws TemplateException
     */
    public void generateJavaMapperFile(String tableName, String prefix, String basePackage) throws IOException, TemplateException, TableModelNullException {
        TableModel tableModel = this.initTableInfo(tableName, prefix);
        Map<String, Object> fillData = this.getJavaFillData(tableModel, basePackage);
        String basePath = this.basePackageToPath(basePackage);
        String javaMapperPath = basePath + "/mapper";
        String outPath = PathUtils.getProjectDir() + "generate/" + javaMapperPath + "/";
        String fileName = tableModel.getUpperCamelModelName() + "Mapper.java";
        this.generateFile(TemplateNameStorage.JAVA_MAPPER_TPL, outPath, fileName, fillData);
    }

    /**
     * 生成XMLMapper文件
     * @throws IOException
     * @throws TemplateException
     */
    public void generateXmlMapperFile(String tableName, String prefix, String basePackage) throws IOException, TemplateException, TableModelNullException {
        TableModel tableModel = this.initTableInfo(tableName, prefix);
        Map<String, Object> fillData = this.getJavaFillData(tableModel, basePackage);
        String outPath = PathUtils.getProjectDir() + "generate/mapper/";
        String fileName = tableModel.getUpperCamelModelName() + "Mapper.xml";
        this.generateFile(TemplateNameStorage.XML_MAPPER_TPL, outPath, fileName, fillData);
    }


    /**
     * 生成Controller文件
     * @throws IOException
     * @throws TemplateException
     */
    public void generateControllerFile(String tableName, String prefix, String basePackage) throws IOException, TemplateException, TableModelNullException {
        TableModel tableModel = this.initTableInfo(tableName, prefix);
        Map<String, Object> fillData = this.getJavaFillData(tableModel, basePackage);
        String basePath = this.basePackageToPath(basePackage);
        String controllerPath = basePath + "/controller";
        String outPath = PathUtils.getProjectDir() + "generate/" + controllerPath + "/";
        String fileName = tableModel.getUpperCamelModelName() + "Controller.java";
        this.generateFile(TemplateNameStorage.CONTROLLER_TPL, outPath, fileName, fillData);
    }


    /**
     * 获取java填充数据
     * @param tableModel
     * @param basePackage
     * @return
     */
    private Map<String, Object> getJavaFillData(TableModel tableModel, String basePackage) {
        Map<String, Object> fillData = new HashMap<>();
        fillData.put("basePackage", basePackage);
        fillData.put("tableName", tableModel.getTableName());
        fillData.put("tableComment", tableModel.getTableComment());
        fillData.put("upperStartModelName", tableModel.getUpperCamelModelName());
        fillData.put("middleLineModelName", tableModel.getMiddleLineModelName());
        fillData.put("camelModelName", tableModel.getCamelModelName());
        fillData.put("columns", tableModel.getColumns());
        return fillData;
    }

    /**
     * basePackage转换为path路径
     * @param basePackage
     * @return
     */
    private String basePackageToPath(String basePackage) {
        return basePackage.replaceAll("\\.", "/");
    }


    /**
     * 初始化table info
     * @param tableName
     * @param prefix
     * @return
     * @throws TableModelNullException
     */
    protected TableModel initTableInfo(String tableName, String prefix) throws TableModelNullException {
        if (StringUtils.isAllEmpty(tableName)) {
            throw new TableModelNullException("table name must be not NULL or BLANK");
        }
        TableModel tableModel;
        if (StringUtils.isAllEmpty(prefix)) {
            tableModel = this.getTableInfoWithoutPrefix(tableName);
        } else {
            tableModel = this.getTableInfo(tableName, prefix);
        }
        return tableModel;
    }


    /**
     * 获取table info
     *
     * @param tableName
     */
    protected TableModel getTableInfoWithoutPrefix(String tableName) throws TableModelNullException {
        TableModel tableModel = new TableModel(tableName);
        this.fillTableInfo(tableModel);
        return tableModel;
    }

    /**
     * 获取table info
     *
     * @param tableName
     */
    protected TableModel getTableInfo(String tableName, String prefix) throws TableModelNullException {
        TableModel tableModel = new TableModel(tableName, prefix);
        this.fillTableInfo(tableModel);
        return tableModel;
    }


    /**
     * 填充table信息
     *
     * @param tableModel
     * @throws TableModelNullException
     */
    protected void fillTableInfo(TableModel tableModel) throws TableModelNullException {
        if (tableModel == null) {
            throw new TableModelNullException("table model must be not NULL");
        }
        Map<String, String> tableComment = this.tableStructureMapper.getTableComment(tableModel.getTableName());
        tableModel.setTableComment(tableComment.get("COMMENTS"));
        List<Map<String, String>> tableColumns = this.tableStructureMapper.selectTableColumns(tableModel.getTableName());
        List<Column> columns = new ArrayList<>();
        for (Map<String, String> tableColumn : tableColumns) {
            Column column = new Column();
            column.setOriginalName(tableColumn.get("COLUMN_NAME"));
            column.setName(StringConvertUtils.underlineToCamel(tableColumn.get("COLUMN_NAME")));
            column.setUpperStartName(StringConvertUtils.underlineToCamelUpperStart(tableColumn.get("COLUMN_NAME")));
            column.setComment(tableColumn.get("COMMENTS"));
            column.setType(tableColumn.get("DATA_TYPE"));
            column.setJavaType(TypeUtils.oracleTypeToJavaType(tableColumn.get("DATA_TYPE")));
            column.setNullable(StringUtils.equals(tableColumn.get("NULLABLE"), "Y"));
            columns.add(column);
        }
        tableModel.setColumns(columns);
    }


    /**
     * 生成文件
     * @param tplName       模板名称
     * @param outPath       输出路径
     * @param fileName      输出文件名称
     * @param fillData      填充数据
     * @throws IOException
     * @throws TemplateException
     */
    protected void generateFile(String tplName, String outPath, String fileName, Map<String, Object> fillData) throws IOException, TemplateException {
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);
        configuration.setDirectoryForTemplateLoading(new File(PathUtils.getTplPath()));
        Template template = configuration.getTemplate(tplName);
        File out = new File(outPath);
        if (!out.exists()) {
            out.mkdirs();
        }
        File generateFile = new File(outPath + fileName);
        Writer outer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(generateFile)));
        template.process(fillData, outer);
        outer.flush();
        outer.close();
    }


}
